using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;

namespace HslControls
{
	/// <summary>
	/// 整个组件的代码辅助工具，提供了一个基础的类库方法
	/// </summary>
	public class HslHelper
	{
		/// <summary>
		/// 从一个矩形的图形中获取菱形的坐标数组
		/// </summary>
		/// <param name="rect">矩形</param>
		/// <returns>数组结果</returns>
		public static Point[] GetRhombusFromRectangle(Rectangle rect)
		{
			return new Point[5]
			{
				new Point(rect.X, rect.Y + rect.Height / 2),
				new Point(rect.X + rect.Width / 2, rect.Y + rect.Height - 1),
				new Point(rect.X + rect.Width - 1, rect.Y + rect.Height / 2),
				new Point(rect.X + rect.Width / 2, rect.Y),
				new Point(rect.X, rect.Y + rect.Height / 2)
			};
		}

		/// <summary>
		/// 计算绘图时的相对偏移值
		/// </summary>
		/// <param name="max">0-100分的最大值，就是指准备绘制的最大值</param>
		/// <param name="min">0-100分的最小值，就是指准备绘制的最小值</param>
		/// <param name="height">实际绘图区域的高度</param>
		/// <param name="value">需要绘制数据的当前值</param>
		/// <returns>相对于0的位置，还需要增加上面的偏值</returns>
		public static float ComputePaintLocationY(int max, int min, int height, int value)
		{
			if ((float)(max - min) == 0f)
			{
				return height;
			}
			return (float)height - (float)(value - min) * 1f / (float)(max - min) * (float)height;
		}

		/// <summary>
		/// 计算绘图时的相对偏移值
		/// </summary>
		/// <param name="max">0-100分的最大值，就是指准备绘制的最大值</param>
		/// <param name="min">0-100分的最小值，就是指准备绘制的最小值</param>
		/// <param name="height">实际绘图区域的高度</param>
		/// <param name="value">需要绘制数据的当前值</param>
		/// <returns>相对于0的位置，还需要增加上面的偏值</returns>
		public static float ComputePaintLocationY(float max, float min, float height, float value)
		{
			if (max - min == 0f)
			{
				return height;
			}
			return height - (value - min) / (max - min) * height;
		}

		/// <summary>
		/// 绘制坐标系中的刻度线
		/// </summary>
		/// <param name="g">绘图对象</param>
		/// <param name="penLine">画坐标轴的画笔</param>
		/// <param name="penDash"></param>
		/// <param name="font"></param>
		/// <param name="brush"></param>
		/// <param name="sf"></param>
		/// <param name="degree"></param>
		/// <param name="max"></param>
		/// <param name="min"></param>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="left"></param>
		/// <param name="right"></param>
		/// <param name="up"></param>
		/// <param name="down"></param>
		public static void PaintCoordinateDivide(Graphics g, Pen penLine, Pen penDash, Font font, Brush brush, StringFormat sf, int degree, int max, int min, int width, int height, int left = 60, int right = 8, int up = 8, int down = 8)
		{
			for (int i = 0; i <= degree; i++)
			{
				int value = (max - min) * i / degree + min;
				int location = (int)ComputePaintLocationY(max, min, height - up - down, value) + up + 1;
				g.DrawLine(penLine, left - 1, location, left - 4, location);
				if (i != 0)
				{
					g.DrawLine(penDash, left, location, width - right, location);
				}
				g.DrawString(value.ToString(), font, brush, new Rectangle(-5, location - font.Height / 2, left, font.Height), sf);
			}
		}

		/// <summary>
		/// 根据指定的方向绘制一个箭头
		/// </summary>
		/// <param name="g"></param>
		/// <param name="brush"></param>
		/// <param name="point"></param>
		/// <param name="size"></param>
		/// <param name="direction"></param>
		public static void PaintTriangle(Graphics g, Brush brush, Point point, int size, GraphDirection direction)
		{
			Point[] points = new Point[4];
			switch (direction)
			{
			case GraphDirection.Leftward:
				points[0] = new Point(point.X, point.Y - size);
				points[1] = new Point(point.X, point.Y + size);
				points[2] = new Point(point.X - 2 * size, point.Y);
				break;
			case GraphDirection.Rightward:
				points[0] = new Point(point.X, point.Y - size);
				points[1] = new Point(point.X, point.Y + size);
				points[2] = new Point(point.X + 2 * size, point.Y);
				break;
			case GraphDirection.Upward:
				points[0] = new Point(point.X - size, point.Y);
				points[1] = new Point(point.X + size, point.Y);
				points[2] = new Point(point.X, point.Y - 2 * size);
				break;
			default:
				points[0] = new Point(point.X - size, point.Y);
				points[1] = new Point(point.X + size, point.Y);
				points[2] = new Point(point.X, point.Y + 2 * size);
				break;
			}
			points[3] = points[0];
			g.FillPolygon(brush, points);
		}

		/// <summary>
		/// 根据指定的方向绘制一个箭头
		/// </summary>
		/// <param name="g"></param>
		/// <param name="brush"></param>
		/// <param name="point"></param>
		/// <param name="size"></param>
		/// <param name="direction"></param>
		public static void PaintTriangle(Graphics g, Brush brush, PointF point, int size, GraphDirection direction)
		{
			PointF[] points = new PointF[4];
			switch (direction)
			{
			case GraphDirection.Leftward:
				points[0] = new PointF(point.X, point.Y - (float)size);
				points[1] = new PointF(point.X, point.Y + (float)size);
				points[2] = new PointF(point.X - (float)(2 * size), point.Y);
				break;
			case GraphDirection.Rightward:
				points[0] = new PointF(point.X, point.Y - (float)size);
				points[1] = new PointF(point.X, point.Y + (float)size);
				points[2] = new PointF(point.X + (float)(2 * size), point.Y);
				break;
			case GraphDirection.Upward:
				points[0] = new PointF(point.X - (float)size, point.Y);
				points[1] = new PointF(point.X + (float)size, point.Y);
				points[2] = new PointF(point.X, point.Y - (float)(2 * size));
				break;
			default:
				points[0] = new PointF(point.X - (float)size, point.Y);
				points[1] = new PointF(point.X + (float)size, point.Y);
				points[2] = new PointF(point.X, point.Y + (float)(2 * size));
				break;
			}
			points[3] = points[0];
			g.FillPolygon(brush, points);
		}

		/// <summary>
		/// 一个通用的数组新增个数方法，会自动判断越界情况，越界的情况下，会自动的截断或是填充 -&gt; 
		/// A common array of new methods, will automatically determine the cross-border situation, in the case of cross-border, will be automatically truncated or filled
		/// </summary>
		/// <typeparam name="T">数据类型</typeparam>
		/// <param name="array">原数据</param>
		/// <param name="data">等待新增的数据</param>
		/// <param name="max">原数据的最大值</param>
		public static void AddArrayData<T>(ref T[] array, T[] data, int max)
		{
			if (data == null || data.Length == 0)
			{
				return;
			}
			if (array.Length == max)
			{
				Array.Copy(array, data.Length, array, 0, array.Length - data.Length);
				Array.Copy(data, 0, array, array.Length - data.Length, data.Length);
			}
			else if (array.Length + data.Length > max)
			{
				T[] tmp2 = new T[max];
				for (int l = 0; l < max - data.Length; l++)
				{
					tmp2[l] = array[l + (array.Length - max + data.Length)];
				}
				for (int k = 0; k < data.Length; k++)
				{
					tmp2[tmp2.Length - data.Length + k] = data[k];
				}
				array = tmp2;
			}
			else
			{
				T[] tmp = new T[array.Length + data.Length];
				for (int j = 0; j < array.Length; j++)
				{
					tmp[j] = array[j];
				}
				for (int i = 0; i < data.Length; i++)
				{
					tmp[tmp.Length - data.Length + i] = data[i];
				}
				array = tmp;
			}
		}

		/// <summary>
		/// 尺寸转换，计算旋转后的尺寸。
		/// </summary>
		/// <param name="size"></param>
		/// <param name="angle"></param>
		/// <returns></returns>
		public static SizeF ConvertSize(SizeF size, float angle)
		{
			Matrix matrix = new Matrix();
			matrix.Rotate(angle);
			PointF[] pts = new PointF[4];
			pts[0].X = (0f - size.Width) / 2f;
			pts[0].Y = (0f - size.Height) / 2f;
			pts[1].X = (0f - size.Width) / 2f;
			pts[1].Y = size.Height / 2f;
			pts[2].X = size.Width / 2f;
			pts[2].Y = size.Height / 2f;
			pts[3].X = size.Width / 2f;
			pts[3].Y = (0f - size.Height) / 2f;
			matrix.TransformPoints(pts);
			float left = float.MaxValue;
			float right = float.MinValue;
			float top = float.MaxValue;
			float bottom = float.MinValue;
			PointF[] array = pts;
			for (int i = 0; i < array.Length; i++)
			{
				PointF pt = array[i];
				if (pt.X < left)
				{
					left = pt.X;
				}
				if (pt.X > right)
				{
					right = pt.X;
				}
				if (pt.Y < top)
				{
					top = pt.Y;
				}
				if (pt.Y > bottom)
				{
					bottom = pt.Y;
				}
			}
			return new SizeF(right - left, bottom - top);
		}

		/// <summary>
		/// 绘制旋转文本
		/// </summary>
		/// <param name="g"></param>
		/// <param name="s"></param>
		/// <param name="font"></param>
		/// <param name="brush"></param>
		/// <param name="point"></param>
		/// <param name="format"></param>
		/// <param name="angle"></param>
		public static void DrawString(Graphics g, string s, Font font, Brush brush, PointF point, StringFormat format, float angle)
		{
			Matrix mtxSave = g.Transform;
			Matrix mtxRotate = g.Transform;
			mtxRotate.RotateAt(angle, point);
			g.Transform = mtxRotate;
			g.DrawString(s, font, brush, point, format);
			g.Transform = mtxSave;
		}

		private static int GetPow(int digit)
		{
			int result = 1;
			for (int i = 0; i < digit; i++)
			{
				result *= 10;
			}
			return result;
		}

		/// <summary>
		/// 获得数据的上限值，这个上限值是自动计算的。
		/// </summary>
		/// <param name="values">数据值</param>
		/// <returns>数据值</returns>
		public static int CalculateMaxSectionFrom(int[] values)
		{
			int max = values.Max();
			if (max <= 5)
			{
				return 5;
			}
			if (max <= 10)
			{
				return 10;
			}
			int digit = max.ToString().Length - 2;
			int head = int.Parse(max.ToString().Substring(0, 2));
			if (head < 12)
			{
				return 12 * GetPow(digit);
			}
			if (head < 14)
			{
				return 14 * GetPow(digit);
			}
			if (head < 16)
			{
				return 16 * GetPow(digit);
			}
			if (head < 18)
			{
				return 18 * GetPow(digit);
			}
			if (head < 20)
			{
				return 20 * GetPow(digit);
			}
			if (head < 22)
			{
				return 22 * GetPow(digit);
			}
			if (head < 24)
			{
				return 24 * GetPow(digit);
			}
			if (head < 26)
			{
				return 26 * GetPow(digit);
			}
			if (head < 28)
			{
				return 28 * GetPow(digit);
			}
			if (head < 30)
			{
				return 30 * GetPow(digit);
			}
			if (head < 40)
			{
				return 40 * GetPow(digit);
			}
			if (head < 50)
			{
				return 50 * GetPow(digit);
			}
			if (head < 60)
			{
				return 60 * GetPow(digit);
			}
			if (head < 80)
			{
				return 80 * GetPow(digit);
			}
			return 100 * GetPow(digit);
		}

		/// <summary>
		/// 获取当前颜色更淡的颜色信息
		/// </summary>
		/// <param name="color">颜色信息</param>
		/// <returns>颜色</returns>
		public static Color GetColorLight(Color color)
		{
			return Color.FromArgb(color.R + (255 - color.R) * 40 / 100, color.G + (255 - color.G) * 40 / 100, color.B + (255 - color.B) * 40 / 100);
		}

		/// <summary>
		/// 获取当前颜色更淡的颜色信息
		/// </summary>
		/// <param name="color">颜色信息</param>
		/// <returns>颜色</returns>
		public static Color GetColorLightFive(Color color)
		{
			return Color.FromArgb(color.R + (255 - color.R) * 50 / 100, color.G + (255 - color.G) * 50 / 100, color.B + (255 - color.B) * 50 / 100);
		}

		/// <summary>
		/// 从字符串表示的点位信息里解析出真正的点位信息
		/// </summary>
		/// <param name="points">字符串的点位</param>
		/// <param name="soureWidth">原来的长度信息</param>
		/// <param name="sourceHeight">原来的高度信息</param>
		/// <param name="width">实际的宽度信息</param>
		/// <param name="height">实际的高度信息</param>
		/// <param name="dx">x偏移量信息</param>
		/// <param name="dy">y偏移量信息</param>
		/// <returns></returns>
		public static PointF[] GetPointsFrom(string points, float soureWidth, float sourceHeight, float width, float height, float dx = 0f, float dy = 0f)
		{
			string[] itemPoint = points.Split(new char[1]
			{
				' '
			}, StringSplitOptions.RemoveEmptyEntries);
			PointF[] ret = new PointF[itemPoint.Length];
			for (int i = 0; i < itemPoint.Length; i++)
			{
				int index = itemPoint[i].IndexOf(',');
				float x = Convert.ToSingle(itemPoint[i].Substring(0, index));
				float y = Convert.ToSingle(itemPoint[i].Substring(index + 1));
				ret[i] = new PointF(width * (x + dx) / soureWidth, height * (y + dy) / sourceHeight);
			}
			return ret;
		}
	}
}
